import mongoengine as db
from app.db import BaseDocument, BaseEmbeddedDocument
from app.db.models.jobs import AlertSettings, PreLogin
from app.libs.enums import TASK_TYPE_TUPLE, TaskStatus


class TaskSettings(BaseEmbeddedDocument):
    meta = {'strict': False}

    interval = db.LongField(default=1)
    enableSitePortraitTriggerMonitor = db.BooleanField(default=False)
    enable = db.BooleanField(default=False)
    name = db.StringField(default="")
    sourceIp = db.StringField(default="")
    target = db.StringField(default="")
    collect = db.DictField(default={})
    alert = db.DictField(default={})
    taskId = db.StringField(default="")
    maxPage = db.LongField(default=0)
    nodeCount = db.IntField(default=0)
    depth = db.IntField(default=0)
    # 运行时间配置
    periodStartTime = db.StringField(default="")
    periodEndTime = db.StringField(default="")
    startAt = db.StringField(default="")
    scanSpeedConfig = db.DictField(default={})


class Warnings(BaseEmbeddedDocument):
    title = db.StringField(default='')
    type = db.StringField()
    category = db.StringField(default='')
    affects = db.StringField()
    description = db.StringField(default='')
    impact = db.StringField(default='')
    level = db.IntField()
    levelName = db.StringField()
    detail = db.DictField(default={})
    addition = db.DictField()
    isResolved = db.BooleanField(default=False)
    ignoredTime = db.DateTimeField(null=True)
    detailText = db.StringField()
    recommendation = db.StringField()
    reference = db.ListField(default=[])
    id = db.StringField()
    _id = db.ObjectIdField()
    extraDetail = db.StringField(default='')
    traffics = db.ListField(default=[])
    foundAt = db.DateTimeField()
    cnvd = db.ListField()
    cve = db.ListField(default=[])
    cnnvd = db.ListField(default=[])
    payload = db.StringField()


class TaskTargetStatus(BaseEmbeddedDocument):
    status = db.StringField(choices=("good", "http_waf_error", "http_code_error", "ping_error", "http_status_error"))
    httpGetSequence = db.DictField()
    detailPing = db.DictField()
    tracePath = db.DictField()


class Result(BaseEmbeddedDocument):
    stats = db.DictField()
    warnings = db.EmbeddedDocumentListField(Warnings)
    warningCount = db.IntField(min_value=0, default=0)
    warningResolvedCount = db.IntField(min_value=0, default=0)
    warningUnresolvedCount = db.IntField(min_value=0, default=0)
    isChanged = db.BooleanField(default=False)
    ipPorts = db.ListField(default=[])
    subDomains = db.ListField(default=[])
    ipBindDomains = db.ListField(default=[])
    addition = db.DictField()
    targetStatus = db.EmbeddedDocumentField(TaskTargetStatus)


class Task(BaseDocument):
    meta = {
        'collection': 'tasks',
        'strict': False
    }

    taskSettings = db.EmbeddedDocumentField(document_type=TaskSettings, default=TaskSettings)
    alertSettings = db.EmbeddedDocumentField(document_type=AlertSettings, default=AlertSettings)
    result = db.EmbeddedDocumentField(document_type=Result, default=Result)
    securityStatus = db.StringField(default='unknown')
    triggerType = db.StringField(default='')
    isSpecialTask = db.BooleanField(default=False)
    status = db.StringField(default='waiting', choices=[item.value for item in TaskStatus])
    progress = db.IntField(default=0, min_value=0, max_value=100)
    isLatestTask = db.BooleanField(default=False)
    isSendAlert = db.BooleanField(default=False)
    domain = db.StringField(default='')
    target = db.StringField(default='')
    sourceIp = db.StringField(default='')
    taskIdx = db.IntField(default=0)
    taskType = db.StringField(default='', choices=TASK_TYPE_TUPLE)
    uid = db.ObjectIdField(required=True)
    name = db.StringField(default='')
    refType = db.StringField(default='')   # refType + refId = jobId
    refId = db.ObjectIdField()
    jobId = db.ObjectIdField(required=True)
    prevTaskId = db.ObjectIdField()
    startTime = db.DateTimeField()
    taskSessionId = db.StringField(default='')
    taskId = db.StringField(default='')
    nextExecDate = db.DateTimeField()
    endTime = db.DateTimeField()
    prelogin = db.EmbeddedDocumentField(default=PreLogin, document_type=PreLogin)
    proxyIp = db.StringField(default='')  # 使用了代理IP监测


class Reason(BaseEmbeddedDocument):
    """忽略原因"""
    # 1 暂不关注， 2 误报， 3 其他
    type = db.IntField(min_value=1, max_value=3)
    # 预留字段， 备注
    note = db.StringField(default="")


class IgnoreWarning(BaseDocument):
    """忽略威胁记录"""
    meta = {
        'collection': 'ignore_warnings',
        'strict': False
    }

    uid = db.ObjectIdField()  # users._id
    taskId = db.ObjectIdField()  # tasks._id
    taskType = db.StringField(choices=TASK_TYPE_TUPLE)  # tasks.taskType
    jobId = db.ObjectIdField()  # jobs._id
    targetUrl = db.StringField()  # jobs.targetUrl
    ignoredTime = db.DateTimeField()
    reason = db.EmbeddedDocumentField(Reason)
    detail = db.EmbeddedDocumentField(Warnings)
    addition = db.DictField(default={})


class TaskSpiderUrl(BaseDocument):
    """任务扫描URL记录，转存引擎scan_spider_urls"""

    meta = {
        'collection': 'task_spider_urls',
        'strict': False
    }

    uid = db.ObjectIdField()  # users._id
    taskObjId = db.ObjectIdField()  # tasks._id
    taskId = db.StringField()  # tasks.taskId
    taskSessionId = db.StringField()  # tasks.taskSessionId
    taskType = db.StringField(choices=TASK_TYPE_TUPLE)  # tasks.taskType
    jobId = db.ObjectIdField()  # jobs._id
    targetUrl = db.StringField()  # jobs.targetUrl
    endTime = db.DateTimeField()  # tasks.endTime
    spiderUrls = db.ListField(default=[], max_length=3000)  # 一条数据最多存3000个url, 超过3000url的存入下一条数据


class ScanPageBaselineEmbeddedDocument(BaseEmbeddedDocument):
    url = db.StringField()
    type = db.IntField(choices=(1, 2, 3))  # 1 表示网页文本, 2 表示网页中的图片资源，3 表示jss、css等其他资源文件
    md5 = db.StringField()
    size = db.IntField()
    content_type = db.StringField()
    content = db.StringField()
    is_error = db.BooleanField()    # waf、识别不了资源类型等
    is_existing = db.BooleanField()     # 是否存在响应或者状态码为404


class ChangeCheckBasepage(BaseDocument):
    """页面变更基准数据"""

    uid = db.ObjectIdField()
    job_id = db.ObjectIdField()
    asset_task_id = db.ObjectIdField()
    task_id = db.StringField()  # scan_tasks.task_id
    url = db.StringField()  # job_id + url 确定唯一一条数据
    status = db.StringField()  # 更新状态
    update_type = db.StringField(default="auto", choices=("auto", "manual"))
    task_session_id = db.StringField(default="")  # change_type == "auto" 时有效， 否则为空值
    change_count = db.IntField(default=1, min_value=1)
    text = db.EmbeddedDocumentField(document_type=ScanPageBaselineEmbeddedDocument)
    structure = db.EmbeddedDocumentField(document_type=ScanPageBaselineEmbeddedDocument)
    resources = db.EmbeddedDocumentListField(document_type=ScanPageBaselineEmbeddedDocument, default=[])
    created_at = db.StringField()
    updated_at = db.StringField()

    meta = {
        'collection': 'change_check_basepages',
        'strict': False
    }
